package com.rgs.base.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.rgs.base.core.util.RgsJson;
import com.rgs.base.redis.serializer.CustomizedRedisCacheManager;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.support.RedisRepositoryFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * @author Ren Gaoshuai
 * @ClassName RedisAutoConfig
 * @date 2021-12-24 16:34
 **/
@Configuration
@EnableCaching
@ConditionalOnProperty(prefix = "spring.redis", name = "enable", havingValue = "true")
@AutoConfigureAfter(RedisRepositoryFactory.class)
@Import(RedisImportSelector.class)
public class RedisAutoConfig {
    private static final Jackson2JsonRedisSerializer REDIS_VALUE_SERIALIZER = new Jackson2JsonRedisSerializer(Object.class);
    private static final RedisSerializer<String> REDIS_KEY_SERIALIZER = new StringRedisSerializer();

    static {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(  LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        RgsJson.config(objectMapper);
        REDIS_VALUE_SERIALIZER.setObjectMapper(objectMapper);
    }

    @Resource
    RedisConnectionFactory connectionFactory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        //key
        redisTemplate.setKeySerializer(REDIS_KEY_SERIALIZER);
        redisTemplate.setHashKeySerializer(REDIS_KEY_SERIALIZER);
        //value
        redisTemplate.setValueSerializer(REDIS_VALUE_SERIALIZER);
        redisTemplate.setHashValueSerializer(REDIS_VALUE_SERIALIZER);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }

    @Bean
    @Primary
    public CacheManager cacheManager() {
        return generateCacheManager(connectionFactory, Duration.ofDays(7));
    }

    @Bean
    public CacheManager oneDayCacheManager() {
        return generateCacheManager(connectionFactory, Duration.ofDays(1));
    }

    @Bean
    public CacheManager monthCacheManager() {
        return generateCacheManager(connectionFactory, Duration.ofDays(31));
    }


    private CacheManager generateCacheManager(RedisConnectionFactory factory, Duration ttl) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(ttl)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(REDIS_KEY_SERIALIZER))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(REDIS_VALUE_SERIALIZER))
                .computePrefixWith(name -> name + ":")
                .disableCachingNullValues();
        return new CustomizedRedisCacheManager(factory, config);
    }

}

